package com.gitee.wsl.android.worker.inspector

import android.os.Build
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.NetworkType
import androidx.work.WorkInfo
import com.gitee.wsl.android.R
import com.gitee.wsl.flow.transform.map.map
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import java.util.EnumSet

internal class WorkDetailsViewModel(
    savedStateHandle: SavedStateHandle,
    private val repository: WorkerDetailsRepository,
) : ViewModel() {
    val workerClassName = requireNotNull(savedStateHandle.get<String>(ARG_WORKER_CLASS_NAME))
    val onError = MutableSharedFlow<Throwable>()
    val workers =
        repository.observeWorkDetails(workerClassName)
            .mapItems { it.toWorkDetailsItem() }
            .catch { e ->
                onError.emit(e)
            }.stateIn(
                scope = viewModelScope + Dispatchers.Default,
                started = SharingStarted.Eagerly,
                initialValue = emptyList(),
            )

    companion object {
        const val ARG_WORKER_CLASS_NAME = "workerClassName"
    }
}


internal inline fun <T, R> Flow<List<T>>.mapItems(crossinline mapper: (T) -> R): Flow<List<R>> =
    map {
        it.map(mapper)
    }

internal fun WorkDetails.toWorkDetailsItem() =
    WorkDetailsItem(
        id = id,
        stateIcon = state.iconResId,
        stateTitle = state.titleResId,
        runAttemptCount = runAttemptCount,
        constraints =
        constraintsSet(
            requiredNetworkType = requiredNetworkType,
            requiresBatteryNotLow = requiresBatteryNotLow,
            requiresCharging = requiresCharging,
            requiresDeviceIdle = requiresDeviceIdle,
            requiresStorageNotLow = requiresStorageNotLow,
        ),
        tags =
        tags.filterNot { it == className }
            .map { ChipModel(title = it, icon = R.drawable.ic_badge_nextcloud) },
        inputData = input.keyValueMap,
        outputData = output.keyValueMap,
        lastEnqueueTime = lastEnqueueTime,
        periodicInterval = intervalDuration,
        stopReasonText = mapStopReason(stopReason),
        periodCount = periodCount,
    )

internal data class WorkDetailsItem(
    val id: String,
    @DrawableRes val stateIcon: Int,
    @StringRes val stateTitle: Int,
    val runAttemptCount: Int,
    val periodCount: Int,
    val constraints: Set<WorkConstraint>,
    val tags: List<ChipModel>,
    val lastEnqueueTime: Long,
    val periodicInterval: Long,
    @StringRes val stopReasonText: Int,
    val inputData: Map<String, Any>,
    val outputData: Map<String, Any>,
)

data class ChipModel(
    val title: CharSequence,
    @DrawableRes val icon: Int,
)

internal enum class WorkConstraint {
    NETWORK_METERED,
    NETWORK_UNMETERED,
    BATTERY,
    CHARGING,
    IDLE,
    STORAGE,
}

private fun mapStopReason(reason: Int): Int = 0

/*
@StringRes
private fun mapStopReason(reason: Int): Int =
    when (reason) {
        STOP_REASON_NOT_STOPPED -> 0
        STOP_REASON_UNKNOWN -> R.string.wi_stop_reason_unknown
        STOP_REASON_CANCELLED_BY_APP -> R.string.wi_stop_reason_cancelled_by_app
        STOP_REASON_PREEMPT -> R.string.wi_stop_reason_preempt
        STOP_REASON_TIMEOUT -> R.string.wi_stop_reason_timeout
        STOP_REASON_DEVICE_STATE -> R.string.wi_stop_reason_device_state
        STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW -> R.string.wi_stop_reason_constraint_battery_not_low
        STOP_REASON_CONSTRAINT_CHARGING -> R.string.wi_stop_reason_constraint_charging
        STOP_REASON_CONSTRAINT_CONNECTIVITY -> R.string.wi_stop_reason_constraint_connectivity
        STOP_REASON_CONSTRAINT_DEVICE_IDLE -> R.string.wi_stop_reason_constraint_device_idle
        STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW -> R.string.wi_stop_reason_constraint_storage_not_low
        STOP_REASON_QUOTA -> R.string.wi_stop_reason_quota
        STOP_REASON_BACKGROUND_RESTRICTION -> R.string.wi_stop_reason_background_restriction
        STOP_REASON_APP_STANDBY -> R.string.wi_stop_reason_app_standby
        STOP_REASON_USER -> R.string.wi_stop_reason_user
        STOP_REASON_SYSTEM_PROCESSING -> R.string.wi_stop_reason_system_processing
        STOP_REASON_ESTIMATED_APP_LAUNCH_TIME_CHANGED -> R.string.wi_stop_reason_estimated_app_launch_time_changed
        else -> 0
    }*/

private fun constraintsSet(
    requiredNetworkType: NetworkType?,
    requiresBatteryNotLow: Boolean,
    requiresCharging: Boolean,
    requiresDeviceIdle: Boolean,
    requiresStorageNotLow: Boolean,
): Set<WorkConstraint> {
    val result = EnumSet.noneOf(WorkConstraint::class.java)
    if (requiredNetworkType == NetworkType.CONNECTED || requiredNetworkType == NetworkType.METERED || requiredNetworkType == NetworkType.NOT_ROAMING) {
        result += WorkConstraint.NETWORK_METERED
    }
    if (requiredNetworkType == NetworkType.UNMETERED || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && requiredNetworkType == NetworkType.TEMPORARILY_UNMETERED)) {
        result += WorkConstraint.NETWORK_UNMETERED
    }
    if (requiresBatteryNotLow) {
        result += WorkConstraint.BATTERY
    }
    if (requiresCharging) {
        result += WorkConstraint.CHARGING
    }
    if (requiresDeviceIdle) {
        result += WorkConstraint.IDLE
    }
    if (requiresStorageNotLow) {
        result += WorkConstraint.STORAGE
    }
    return result
}

//@get:DrawableRes
private val WorkInfo.State?.iconResId: Int
    get() = 0
/*get() =
    when (this) {
        WorkInfo.State.ENQUEUED -> R.drawable.wi_ic_state_queued
        WorkInfo.State.RUNNING -> R.drawable.wi_ic_state_running
        WorkInfo.State.SUCCEEDED -> R.drawable.wi_ic_state_succeeded
        WorkInfo.State.FAILED -> R.drawable.wi_ic_state_failed
        WorkInfo.State.BLOCKED -> R.drawable.wi_ic_state_blocked
        WorkInfo.State.CANCELLED -> R.drawable.wi_ic_state_cancelled
        else -> R.drawable.wi_ic_state_unknown
    }*/

//@get:StringRes
private val WorkInfo.State?.titleResId: Int
    get() = 0
/*get() =
    when (this) {
        WorkInfo.State.ENQUEUED -> R.string.wi_state_queued
        WorkInfo.State.RUNNING -> R.string.wi_state_running
        WorkInfo.State.SUCCEEDED -> R.string.wi_state_succeeded
        WorkInfo.State.FAILED -> R.string.wi_state_failed
        WorkInfo.State.BLOCKED -> R.string.wi_state_blocked
        WorkInfo.State.CANCELLED -> R.string.wi_state_cancelled
        else -> R.string.wi_state_unknown
    }*/